package com.jiuair.cloud.data.revenue.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jiuair.cloud.data.revenue.config.ServerConfig;
import com.jiuair.cloud.data.revenue.domain.Token;
import com.jiuair.cloud.data.revenue.entity.UsrUser;
import com.jiuair.cloud.data.revenue.mapper.UsrUserMapper;
import com.jiuair.cloud.data.revenue.mybatis.base.service.impl.BaseServiceImpl;
import com.jiuair.cloud.data.revenue.result.ResponseEnum;
import com.jiuair.cloud.data.revenue.result.UrlEnum;
import com.jiuair.cloud.data.revenue.service.UsrUserService;
import com.jiuair.cloud.data.revenue.utils.BeanUtils;
import com.jiuair.cloud.data.revenue.utils.MD5Util;
import com.jiuair.cloud.data.revenue.utils.RedisUtil;
import com.jiuair.cloud.data.revenue.vo.LoginUserDTO;
import com.jiuair.cloud.data.revenue.vo.LoginUserVO;
import com.jiuair.cloud.data.revenue.vo.ResponseVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;

import static com.jiuair.cloud.data.revenue.config.OAuth2Config.CLIENT_ID;
import static com.jiuair.cloud.data.revenue.config.OAuth2Config.CLIENT_SECRET;
import static com.jiuair.cloud.data.revenue.config.OAuth2Config.GRANT_TYPE;

/**
 * 用户信息表 服务实现类
 *
 * @author deng
 * @date 2020-08-27
 */
@Service
//@Transactional(rollbackFor = Exception.class)
public class UsrUserServiceImpl extends BaseServiceImpl<UsrUserMapper, UsrUser> implements UsrUserService {
    @Autowired
    private UsrUserService usrUserService;
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ServerConfig serverConfig;

    @Autowired
    private RedisUtil redisUtil;


    @Override
    public UsrUser findUserByAccount(String username) {
        QueryWrapper<UsrUser> queryWrapper = new QueryWrapper();
        queryWrapper.lambda()
                .eq(UsrUser::getUserName, username);
        return getOne(queryWrapper);
    }


    @Override
    public ResponseVO login(LoginUserDTO loginUserDTO) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        //  PasswordEncoder md5PasswordEncoder = new Md5PasswordEncoder();
        // String encode = md5PasswordEncoder.encode(loginUserDTO.getPassword());
        UsrUser user = usrUserService.findUserByAccount(loginUserDTO.getUsername());
        if (user == null) {
            return ResponseVO.errorParams("此用户不存在");
        }

        String usePasscode = MD5Util.code(user.getPasswd());
        user.setPassword(usePasscode);
        user.setId(user.getId());
        usrUserService.updateById(user);
        String codepass = MD5Util.codepass(loginUserDTO.getPassword(), user.getPasswd());
        //   String encode = MD5Util.encode(loginUserDTO.getPassword());
        // String encode = MD5Util.getEncryptedPwd(loginUserDTO.getPassword());
        //  encode="111";
        //  String  codepass=   "25711DC62F49B9DE2998726EE128538B";
        paramMap.add("client_id", CLIENT_ID);
        paramMap.add("client_secret", CLIENT_SECRET);
        paramMap.add("username", loginUserDTO.getUsername());
        paramMap.add("grant_type", GRANT_TYPE[0]);
        paramMap.add("password", codepass);
        Token token = null;
        /**
         * 1
         * 2
         */

        try {
            //因为oauth2本身自带的登录接口是"/oauth/token"，并且返回的数据类型不能按我们想要的去返回
            //但是我的业务需求是，登录接口是"user/login"，由于我没研究过要怎么去修改oauth2内部的endpoint配置
            //所以这里我用restTemplate(HTTP客户端)进行一次转发到oauth2内部的登录接口，比较简单粗暴
            token = restTemplate.postForObject(serverConfig.getUrl() + UrlEnum.LOGIN_URL.getUrl(), paramMap, Token.class);

            LoginUserVO loginUserVO = redisUtil.get(token.getValue(), LoginUserVO.class);
            if (loginUserVO != null) {
                //登录的时候，判断该用户是否已经登录过了
                //如果redis里面已经存在该用户已经登录过了的信息
                //我这边要刷新一遍token信息，不然，它会返回上一次还未过时的token信息给你
                //不便于做单点维护
                token = oauthRefreshToken(loginUserVO.getRefreshToken());
                redisUtil.deleteCache(loginUserVO.getAccessToken());
            }
        } catch (RestClientException e) {
            try {
                e.printStackTrace();
                //此处应该用自定义异常去返回，在这里我就不去具体实现了
                //throw new Exception("username or password error");
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
        //这里我拿到了登录成功后返回的token信息之后，我再进行一层封装，最后返回给前端的其实是LoginUserVO
        LoginUserVO loginUserVO = new LoginUserVO();
        UsrUser userPO = findUserByAccount(loginUserDTO.getUsername());
        BeanUtils.copyPropertiesIgnoreNull(userPO, loginUserVO);
        loginUserVO.setPassword("");
        loginUserVO.setAccessToken(token.getValue());
        loginUserVO.setAccessTokenExpiresIn(token.getExpiresIn());
        loginUserVO.setAccessTokenExpiration(token.getExpiration());
        loginUserVO.setExpired(token.isExpired());
        loginUserVO.setScope(token.getScope());
        loginUserVO.setTokenType(token.getTokenType());
        loginUserVO.setRefreshToken(token.getRefreshToken().getValue());
        loginUserVO.setRefreshTokenExpiration(token.getRefreshToken().getExpiration());
        //存储登录的用户
        redisUtil.set(loginUserVO.getAccessToken(), loginUserVO, TimeUnit.HOURS.toSeconds(1));
        return ResponseVO.success(loginUserVO);
    }

    /**
     * @description oauth2客户端刷新token
     * @param refreshToken
     * @date 2019/03/05 14:27:22
     * @author Zhifeng.Zeng
     * @return
     */
    private Token oauthRefreshToken(String refreshToken) {
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("client_id", CLIENT_ID);
        paramMap.add("client_secret", CLIENT_SECRET);
        paramMap.add("refresh_token", refreshToken);
        paramMap.add("grant_type", GRANT_TYPE[1]);
        Token token = null;
        try {
            token = restTemplate.postForObject(serverConfig.getUrl() + UrlEnum.LOGIN_URL.getUrl(), paramMap, Token.class);
        } catch (RestClientException e) {
            try {
                //此处应该用自定义异常去返回，在这里我就不去具体实现了
                throw new Exception(ResponseEnum.REFRESH_TOKEN_INVALID.getMessage());
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
        return token;
    }

}
